use axum::Router;
use nacos_sdk::api::naming::ServiceInstance;
use std::net::SocketAddr;
use std::time::Duration;
use tokio::signal;
use tower::ServiceBuilder;
use tower_http::timeout::TimeoutLayer;
use tower_http::trace::TraceLayer;

mod controller;
mod nacos;

#[tokio::main]
pub async fn start() {
    let api_routes = controller::routing::set_routes();

    // IMPORTANT!!!
    // Want to apply with_state without error you must make sure each setting routes functions return the same date type as your state, here is Router<Arc<AppState>>.
    let app = Router::new()
        .nest("/api", api_routes)
        .layer(
            ServiceBuilder::new()
                .layer(TraceLayer::new_for_http())
                // Graceful shutdown will wait for outstanding requests to complete. Add a timeout so
                // requests don't hang forever.
                .layer(TimeoutLayer::new(Duration::from_secs(10))),
        )
        .into_make_service_with_connect_info::<SocketAddr>();

    let nacos_service = nacos::nacos_service::NacoService::new();
    let listener = tokio::net::TcpListener::bind("0.0.0.0:8089").await.unwrap();
    let service_instance: ServiceInstance;
    // make_request(&nacos_service).await;

    // This code will only compile and run in debug builds.
    #[cfg(debug_assertions)]
    {
        service_instance = nacos_service.register_instance().await;

        println!("listening on {}{}", listener.local_addr().unwrap(), "/api");
    }

    // axum::serve(listener, app).await.unwrap();

    // Run the server with graceful shutdown
    axum::serve(listener, app)
        .with_graceful_shutdown(shutdown_signal(nacos_service, service_instance))
        .await
        .unwrap();
}

async fn make_request(nacos_service: &nacos::nacos_service::NacoService) {
    let (ip, port) = nacos_service.get_service_info("order-service").await;

    match reqwest::get(format!("http://{}:{}/api/orders", ip, port)).await {
        Ok(respons) => println!("Orders: {}", respons.text().await.unwrap()),
        Err(_) => println!("Error getting orders"),
    };
}

async fn shutdown_signal(
    nacos_service: nacos::nacos_service::NacoService,
    service_instance: ServiceInstance,
) {
    nacos_service.deregister_instance(service_instance).await;

    let ctrl_c = async {
        signal::ctrl_c()
            .await
            .expect("failed to install Ctrl+C handler");
    };

    #[cfg(unix)]
    let terminate = async {
        signal::unix::signal(signal::unix::SignalKind::terminate())
            .expect("failed to install signal handler")
            .recv()
            .await;
    };

    #[cfg(not(unix))]
    let terminate = std::future::pending::<()>();

    tokio::select! {
        _ = ctrl_c => {},
        _ = terminate => {},
    }
}
